package top.cluski.feieprint.api.teable;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.dtflys.forest.Forest;
import com.dtflys.forest.exceptions.ForestRuntimeException;
import com.dtflys.forest.http.ForestRequest;
import com.dtflys.forest.http.ForestResponse;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import top.cluski.feieprint.api.teable.entity.Filter;
import top.cluski.feieprint.api.teable.entity.RecordAddDTO;
import top.cluski.feieprint.api.teable.entity.RecordAddEntity;
import top.cluski.feieprint.api.teable.entity.RecordEntity;
import top.cluski.feieprint.config.TeableConfig;

import java.io.File;
import java.util.*;

@Slf4j
@Component
public class TeableApi {
    @Resource
    TeableConfig teableConfig;

    public static String DOMAIN = "https://app.teable.cn";

    public List<String> addRecords(@Nonnull List<RecordAddEntity> records) {
        if (records.isEmpty()) {
            return new ArrayList<>();
        }
        List<HashMap<String, RecordAddEntity>> mapRecords = records.stream().map(item -> {
            HashMap<String, RecordAddEntity> map = new HashMap<>();
            map.put("fields", item);
            return map;
        }).toList();

        HashMap<String, Object> body = new HashMap<>();
        body.put("typecast", true);
        body.put("fieldKeyType", "name");
        body.put("records", mapRecords);
        String resp = Forest.post(StrUtil.format("{}/api/table/{}/record", DOMAIN, teableConfig.getTableId()))
                .addHeader("Authorization", "Bearer " + teableConfig.getToken())
                .contentTypeJson()
                .addBody(JSONObject.toJSONString(body))
                .executeAsString();
        JSONArray resRecords = JSONObject.parseObject(resp).getJSONArray("records");

        ArrayList<String> resRecordIdList = new ArrayList<>();
        for (int i = 0; i < resRecords.size(); i++) {
            resRecordIdList.add(resRecords.getJSONObject(i).getString("id"));
        }
        return resRecordIdList;
    }

    public List<String> addRecordsCommon(@Nonnull List<RecordAddDTO> records) {
        return this.addRecords(teableConfig.getTableId(), records);
    }

    public List<String> addRecords(@Nonnull String tableId, List<RecordAddDTO> records) {
        Assert.notEmpty(tableId, "table id can not be empty");
        Assert.notEmpty(records, "records can not be empty");
        if (records.isEmpty()) {
            return new ArrayList<>();
        }

        HashMap<String, Object> body = new HashMap<>();
        body.put("typecast", true);
        body.put("fieldKeyType", "name");
        body.put("records", records);
        String resp = Forest.post(StrUtil.format("{}/api/table/{}/record", DOMAIN, tableId))
                .addHeader("Authorization", "Bearer " + teableConfig.getToken())
                .contentTypeJson()
                .addBody(JSONObject.toJSONString(body))
                .executeAsString();
        JSONArray resRecords = JSONObject.parseObject(resp).getJSONArray("records");

        ArrayList<String> resRecordIdList = new ArrayList<>();
        for (int i = 0; i < resRecords.size(); i++) {
            resRecordIdList.add(resRecords.getJSONObject(i).getString("id"));
        }
        return resRecordIdList;
    }

    public void updateRecord(@Nonnull String tableId, @Nonnull String recordId, @Nonnull RecordAddDTO record) {
        Assert.notEmpty(tableId, "table id can not be empty");
        Assert.notEmpty(recordId, "record id can not be empty");
        Assert.notNull(record, "record can not be null");

        Map<String, Object> body = new HashMap<>();
        body.put("typecast", true);
        body.put("fieldKeyType", "name");
        body.put("record", record);
        Forest.patch(StrUtil.format("{}/api/table/{}/record/{}", DOMAIN, tableId, recordId))
                .addHeader("Authorization", "Bearer " + teableConfig.getToken())
                .contentTypeJson()
                .addBody(JSONObject.toJSONString(body))
                .executeAsString();
    }

    public void uploadAttachment(String recordId, String fileUrl) {
        String resp = Forest.post(StrUtil.format("{}/api/table/{}/record/{}/{}/uploadAttachment",
                        DOMAIN, teableConfig.getTableId(), recordId, teableConfig.getPicFieldId()))
                .addHeader("Authorization", "Bearer " + teableConfig.getToken())
                .contentTypeMultipartFormData()
                .addBody("fileUrl", fileUrl)
                .onError((ForestRuntimeException ex, ForestRequest request, ForestResponse response) -> {
                    String content = response.getContent();
                    JSONObject jsonObject = JSONObject.parseObject(content);
                    if ("payment_required".equals(jsonObject.getString("code"))) {
                        return;
                    }
                    throw ex;
                })
                .executeAsString();
    }

    public void uploadAttachment(String recordId, File file) {
        String resp = Forest.post(StrUtil.format("{}/api/table/{}/record/{}/{}/uploadAttachment",
                        DOMAIN, teableConfig.getTableId(), recordId, teableConfig.getPicFieldId()))
                .addHeader("Authorization", "Bearer " + teableConfig.getToken())
                .contentTypeMultipartFormData()
                .addFile("file", file, "image/jpeg")
                .onError((ForestRuntimeException ex, ForestRequest request, ForestResponse response) -> {
                    String content = response.getContent();
                    JSONObject jsonObject = JSONObject.parseObject(content);
                    if ("payment_required".equals(jsonObject.getString("code"))) {
                        return;
                    }
                    throw ex;
                })
                .executeAsString();
    }

    public RecordEntity getOneRecord(String recordId) {
        String resp = Forest.get(StrUtil.format("{}/api/table/{}/record/{}",
                        DOMAIN, teableConfig.getTableId(), recordId))
                .addHeader("Authorization", "Bearer " + teableConfig.getToken())
                .executeAsString();
        return JSONObject.parseObject(resp, RecordEntity.class);
    }

    public RecordEntity getFirstRecord() {
        String resp = Forest.get(StrUtil.format("{}/api/table/{}/record?cellFormat=json&fieldKeyType=name&take=1&skip=0",
                        DOMAIN, teableConfig.getTableId()))
                .addHeader("Authorization", "Bearer " + teableConfig.getToken())
                .executeAsString();
        JSONObject jsonObject = JSONObject.parseObject(resp);
        JSONArray records = jsonObject.getJSONArray("records");
        return records.getJSONObject(0).to(RecordEntity.class);

    }

    public RecordEntity filterByYouzanOrderId(String orderId) {
        String resp = Forest.get(StrUtil.format("{}/api/table/{}/record?filter={\"conjunction\":\"and\",\"filterSet\":[{\"fieldId\":\"有赞订单号\",\"operator\":\"is\",\"value\":\"{}\"}]}",
                        DOMAIN, teableConfig.getTableId(), orderId))
                .addHeader("Authorization", "Bearer " + teableConfig.getToken())
                .executeAsString();
        JSONObject jsonObject = JSONObject.parseObject(resp);
        JSONArray records = jsonObject.getJSONArray("records");
        return records.isEmpty() ? null : records.getJSONObject(0).to(RecordEntity.class);
    }

    public List<RecordEntity> getRecords() {
        return this.getRecords(null);
    }

    public List<RecordEntity> getRecords(@Nullable Filter filter) {
        return this.getRecords(teableConfig.getTableId(), filter);
    }

    public List<RecordEntity> getRecords(@Nonnull String tableId, @Nullable Filter filter) {
        Assert.notEmpty(tableId, "table id can not be empty");

        ForestRequest<?> forestRequest = Forest.get(StrUtil.format("{}/api/table/{}/record", DOMAIN, tableId))
                .addHeader("Authorization", "Bearer " + teableConfig.getToken());
        forestRequest.addQuery("take", 400);
        if (filter != null) {
            forestRequest.addQuery("filter", JSONObject.toJSONString(filter));
        }
        String resp = forestRequest.executeAsString();
        JSONObject jsonObject = JSONObject.parseObject(resp);
        return jsonObject.getObject("records", new TypeReference<>() {
        });
    }

    public void delRecord(String id) {
        this.delRecords(teableConfig.getTableId(), Collections.singletonList(id));
    }

    public void delRecords(@Nonnull String tableId, @Nonnull List<String> ids) {
        ForestResponse resp = Forest.delete(StrUtil.format("{}/api/table/{}/record", DOMAIN, tableId))
                .addHeader("Authorization", "Bearer " + teableConfig.getToken())
                .addArrayQuery("recordIds", ids)
                .executeAsResponse();
        if (!resp.status_2xx()) {
            log.error("del record error. status_code={} content={}", resp.getStatusCode(), resp.getContent());
            throw new RuntimeException(resp.getContent());
        }
    }

    public RecordEntity getRecord(String tableId, String recordId) {
        String resp = Forest.get(StrUtil.format("{}/api/table/{}/record/{}",
                        DOMAIN, tableId, recordId))
                .addHeader("Authorization", "Bearer " + teableConfig.getToken())
                .setRetryEnabled(true)
                .executeAsString();
        return JSONObject.parseObject(resp, RecordEntity.class);
    }
}
